/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse.query;

import java.io.*;

import nfse.*;
import nfse.result.*;

import org.xml.sax.XMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;

public class QueryHandler extends DefaultHandler {
	private XMLReader xr = null;

	private Query query;

	private QueryServer queryServer = null;

	boolean foundError = false;

	public QueryHandler(QueryServer queryServer) {
		try {
			this.queryServer = queryServer;
			xr = XMLReaderFactory.createXMLReader();
			xr.setContentHandler(this);
			xr.setErrorHandler(this);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Query getQuery() {
		if (!foundError)
			return this.query;
		return null;
	}

	public void parse(InputStream in) {
		try {
			String query = "";
			LineNumberReader r = new LineNumberReader(new InputStreamReader(in));
			String line = r.readLine();
			while (line != null) {
				System.out.println(line);
				int termIdx = line.indexOf("<--DiSARM: end query-->");
				if (termIdx == 0) {
					break;
				} else if (termIdx > 0) {
					query += line.substring(0, termIdx);
					break;
				} else {
					query += line;
				}
				/*
				 * if (line.startsWith(" <--NetFSE: end query-->")) break; else
				 * query += line;
				 */
				line = r.readLine();
			}
			System.out.println("Parsing query: '" + query + "'");
			StringReader str = new StringReader(query);
			xr.parse(new InputSource(str));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// //////////////////////////////////////////////////////////////////
	// Event handlers.
	// //////////////////////////////////////////////////////////////////
	public void startDocument() {
		// System.out.println("Start of query");
		// query = new Query();
	}

	public void endDocument() {
		// System.out.println("End of query");
		// process();
	}

	public void startElement(String uri, String name, String qName,
			Attributes atts) {
		try {
			if ("".equals(uri)) {
				if (qName.compareTo("Query") == 0) {
					boolean hadID = false;
					if (query == null) { // this is the top of the query tree
						query = new Query(queryServer.nextQueryID());
					} else { // this is a subquery, set it's parent to the
						// current query
						Query temp = query;
						query = new Query(queryServer.nextQueryID());
						query.setParent(temp);
					}
					int sensor = -1;
					int type = NetFSE.TYPE_ALL;
					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("date") == 0) {
							String[] dates = attrValue.split("~");
							if (dates.length == 2) {
								DateRange dr = new DateRange(QueryDate
										.processDate(dates[0]));
								dr.setEndDate(QueryDate.processDate(dates[1]));
								query.addDateRange(dr);
							} else {
								query.addDate(QueryDate.processDate(attrValue));
							}
						} else if (attrName.compareTo("user") == 0) {
							query.setUser(attrValue);
						} else if (attrName.compareTo("password") == 0) {
							query.setPassword(attrValue);
						} else if (attrName.compareTo("userip") == 0) {
							query.setUserIP(attrValue);
						} else if (attrName.compareTo("sessionid") == 0) {
							query.setSessionID(attrValue);
						} else if (attrName.compareTo("id") == 0) {
							hadID = true;
						} else if (attrName.compareTo("distribute") == 0) {
							if (attrValue.toUpperCase().compareTo("TRUE") == 0) {
								query.setDistribute(true);
							} else {
								query.setDistribute(false);
							}
						} else if (attrName.compareTo("type") == 0) {
							type = Integer.parseInt(attrValue);
						} else if (attrName.compareTo("sensor") == 0) {
							sensor = Integer.parseInt(attrValue);
						} else {
							int field = NetFSE.getFieldType(attrName);
							if (field <= 0)
								throw (new QueryException(
										"Invalid field name specified: '"
												+ attrName + "'"));
							boolean found = false;
							for (int j = 1; j < NetFSE.PREDICATES.length; j++) {
								if (attrValue.startsWith(NetFSE.PREDICATES[j])) {
									found = true;
									Criteria c = new Criteria();
									c.field = NetFSE.getFieldType(attrName);
									c.predicate = j;
									c.value1 = attrValue
											.substring(NetFSE.PREDICATES[j]
													.length());
									query.addCriteria(c);
									// System.out.println("Added criteria");
									break;
								}
							}
							if (!found) { // Could be PREDICATE_EQUAL_TO or
								// PREDICATE_RANGE
								int tilLoc = attrValue.indexOf("~");
								if (tilLoc > 0) { // PREDICATE_RANGE
									Criteria c = new Criteria();
									c.field = NetFSE.getFieldType(attrName);
									c.predicate = NetFSE.PREDICATE_RANGE;
									c.value1 = attrValue.substring(0, tilLoc);
									c.value2 = attrValue.substring(tilLoc + 1);
									query.addCriteria(c);
								} else { // PREDICATE_EQUAL_TO
									Criteria c = new Criteria();
									c.field = NetFSE.getFieldType(attrName);
									c.predicate = NetFSE.PREDICATE_EQUAL_TO;
									c.value1 = attrValue
											.substring(NetFSE.PREDICATES[NetFSE.PREDICATE_EQUAL_TO]
													.length() - 1);
									query.addCriteria(c);
								}
							}
						}
					}
					if (!hadID) {
						query.setReply(true);
					}
					Data data = new Data();
					data.type = type;
					data.sensor = sensor;
					query.setData(data);
				} else if (qName.compareTo("Join") == 0) {
					// query.newJoin();
				} else if (qName.compareTo("SearchField") == 0) {
					Criteria c = new Criteria();
					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("name") == 0) {
							for (int x = 0; x < NetFSE.FIELD_NAMES.length; x++) {
								if (NetFSE.FIELD_NAMES[x].toUpperCase()
										.compareTo(attrValue.toUpperCase()) == 0) {
									c.field = x;
									break;
								}
							}
						} else if (attrName.compareTo("predicate") == 0) {
							for (int x = 0; x < NetFSE.PREDICATES.length; x++) {
								if (NetFSE.PREDICATES[x].compareTo(attrValue) == 0) {
									c.predicate = x;
									break;
								}
							}
						} else if (attrName.compareTo("value") == 0) {
							c.value1 = attrValue;
						} else if (attrName.compareTo("lower") == 0) {
							c.value1 = attrValue;
						} else if (attrName.compareTo("upper") == 0) {
							c.value2 = attrValue;
						}
					}
					query.addCriteria(c);

				} else if (qName.compareTo("Data") == 0) {
					// Types of data to be returned
					Data type = new Data();
					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("type") == 0) {
							type.type = Integer.parseInt(attrValue);
						} else if (attrName.compareTo("sensor") == 0) {
							type.sensor = Integer.parseInt(attrValue);
						} else if (attrName.compareTo("limit") == 0) {
							type.limit = Integer.parseInt(attrValue);
						}
					}
					query.addData(type);
					// System.out.println(type.toXML());
				} else if (qName.compareTo("Timestamp") == 0) {
					TimestampRange tsr = null;
					String startTS = null;
					String endTS = null;

					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("start") == 0)
							startTS = attrValue;
						else if (attrName.compareTo("end") == 0)
							endTS = attrValue;
					}

					// if (startTS == null)
					// startTS = endTS;
					// if (endTS == null)
					// endTS = startTS;
					if (startTS == null)
						throw (new QueryException(
								"Missing values in Timestamp attribute."));
					tsr = TimestampRange.generate(startTS, endTS);
					if (tsr != null) {
						query.addTimestampRange(tsr);
						// System.out.println("ADDED TSR: " + tsr.toXML());
					}
				} else if (qName.compareTo("MarshalledResult") == 0) {
					MarshalledResult result = new MarshalledResult();
					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("host") == 0)
							result.host = attrValue;
						if (attrName.compareTo("port") == 0)
							result.port = Integer.parseInt(attrValue);
						if (attrName.compareTo("query") == 0)
							result.queryID = Integer.parseInt(attrValue);
					}
					if (result.queryID == -1) {
						throw (new QueryException(
								"Invalid marshalled result specified. Query ID was -1."));
					}
					query.setResult(result);
				} else if (qName.compareTo("SocketResult") == 0) {
					SocketResult result = new SocketResult();
					for (int i = 0; i < atts.getLength(); i++) {
						String attrName = atts.getLocalName(i);
						String attrValue = atts.getValue(i);
						if (attrName.compareTo("host") == 0)
							result.host = attrValue;
						if (attrName.compareTo("port") == 0)
							result.port = Integer.parseInt(attrValue);
					}
					query.setResult(result);
				} 

			} else {
				// System.out.println("Start element: {" + uri + "}" + name);
			}

		} catch (Exception E) {
			E.printStackTrace();
			foundError = true;
		}
	}

	public void endElement(String uri, String name, String qName) {
		try {
			if ("".equals(uri)) {
				// System.out.println("End element: " + qName);
				if (qName.compareTo("Query") == 0) {
					
					if (query.hasParent()) {
						Query parent = query.getParent();
						// if (!parent.joinPending())
						parent.addSubquery(query);
						// else
						// parent.addToJoin(query);
						query = query.getParent();
					}
				}
				// else if (qName.compareTo("Join") == 0) {
				// if (query.joinPending())
				// query.closeJoin();
				// }
			} else {
				// System.out.println("End element: {" + uri + "}" + name);
			}
		} catch (Exception E) {
			E.printStackTrace();
			foundError = true;
		}
	}

}